#include "w25q64.h"
#include "main.h"
#include <stdio.h>

#define SPI_FLASH_CS_LOW()      HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_RESET);
#define SPI_FLASH_CS_HIGH()     HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_GPIO_Port, W25Q64_CHIP_SELECT_Pin, GPIO_PIN_SET);

/**
 * @brief    SPI发送指定长度的数据
 * @param    buf  —— 发送数据缓冲区首地址
 * @param    size —— 要发送数据的字节数
 * @retval   成功返回HAL_OK
 */
static HAL_StatusTypeDef SPI_Transmit(uint8_t* send_buf, uint16_t size)
{
    return HAL_SPI_Transmit(&hspi1, send_buf, size, 100);
}
/**
 * @brief   SPI接收指定长度的数据
 * @param   buf  —— 接收数据缓冲区首地址
 * @param   size —— 要接收数据的字节数
 * @retval  成功返回HAL_OK
 */
static HAL_StatusTypeDef SPI_Receive(uint8_t* recv_buf, uint16_t size)
{
   return HAL_SPI_Receive(&hspi1, recv_buf, size, 100);
}
/**
 * @brief   SPI在发送数据的同时接收指定长度的数据
 * @param   send_buf  —— 接收数据缓冲区首地址
 * @param   recv_buf  —— 接收数据缓冲区首地址
 * @param   size —— 要发送/接收数据的字节数
 * @retval  成功返回HAL_OK
 */
static __attribute__((unused)) HAL_StatusTypeDef SPI_TransmitReceive(uint8_t* send_buf, uint8_t* recv_buf, uint16_t size)
{
   return HAL_SPI_TransmitReceive(&hspi1, send_buf, recv_buf, size, 100);
}

/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))
static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;   
/**
  * @brief  等待超时回调函数
  * @param  None.
  * @retval None.
  */
static  uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
  /* 等待超时后的处理,输出错误信息 */
  printf("SPI 等待超时!errorCode = %d",errorCode);
  return 0;
}
 /**
  * @brief  使用SPI发送一个字节的数据
  * @param  byte：要发送的数据
  * @retval 返回接收到的数据
  */
uint8_t SPI_FLASH_SendByte(uint8_t byte)
{
  SPITimeout = SPIT_FLAG_TIMEOUT;

  /* 等待发送缓冲区为空，TXE事件 */
  while (__HAL_SPI_GET_FLAG( &hspi1, SPI_FLAG_TXE ) == RESET)
   {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
   }

  /* 写入数据寄存器，把要写入的数据写入发送缓冲区 */
  WRITE_REG(hspi1.Instance->DR, byte);

  SPITimeout = SPIT_FLAG_TIMEOUT;

  /* 等待接收缓冲区非空，RXNE事件 */
  while (__HAL_SPI_GET_FLAG( &hspi1, SPI_FLAG_RXNE ) == RESET)
   {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
   }

  /* 读取数据寄存器，获取接收缓冲区数据 */
  return READ_REG(hspi1.Instance->DR);
}

/**
 * @brief   读取Flash内部的ID
 * @param   none
 * @retval  成功返回device_id
 */
uint16_t W25QXX_ReadID(void)
{
    uint8_t recv_buf[2] = {0};    //recv_buf[0]存放Manufacture ID, recv_buf[1]存放Device ID
    uint16_t device_id = 0;
    uint8_t send_data[4] = {ManufactDeviceID_CMD,0x00,0x00,0x00};   //待发送数据，命令+地址
    
    /* 使能片选 */
    SPI_FLASH_CS_LOW();
    
    /* 发送并读取数据 */
    if (HAL_OK == SPI_Transmit(send_data, 4)) 
    {
        if (HAL_OK == SPI_Receive(recv_buf, 2)) 
        {
            device_id = (recv_buf[0] << 8) | recv_buf[1];
        }
    }
    
    /* 取消片选 */
    SPI_FLASH_CS_HIGH();
    
    return device_id;
}
/**
 * @brief     读取W25QXX的状态寄存器，W25Q64一共有2个状态寄存器
 * @param     reg  —— 状态寄存器编号(1~2)
 * @retval    状态寄存器的值
 */
static uint8_t W25QXX_ReadSR(uint8_t reg)
{
    uint8_t result = 0; 
    uint8_t send_buf[4] = {0x00,0x00,0x00,0x00};
    switch(reg)
    {
        case 1:
            send_buf[0] = READ_STATU_REGISTER_1;
        case 2:
            send_buf[0] = READ_STATU_REGISTER_2;
        case 0:
        default:
            send_buf[0] = READ_STATU_REGISTER_1;
    }
    
    /* 使能片选 */
    SPI_FLASH_CS_LOW();
    
    if (HAL_OK == SPI_Transmit(send_buf, 4)) 
    {
        if (HAL_OK == SPI_Receive(&result, 1)) 
        {
            /* 取消片选 */
            SPI_FLASH_CS_HIGH();
            
            return result;
        }
    }
    
    /* 取消片选 */
    SPI_FLASH_CS_HIGH();

    return 0;
}
/**
 * @brief   阻塞等待Flash处于空闲状态
 * @param   none
 * @retval  none
 */
static void W25QXX_Wait_Busy(void)
{
    while((W25QXX_ReadSR(1) & 0x01) == 0x01); // 等待BUSY位清空
}
/**
 * @brief    W25QXX写使能,将S1寄存器的WEL置位
 * @param    none
 * @retval
 */
void W25QXX_Write_Enable(void)
{
    uint8_t cmd= WRITE_ENABLE_CMD;
    
    SPI_FLASH_CS_LOW();
    
    SPI_Transmit(&cmd, 1);
    
    SPI_FLASH_CS_HIGH();
    
    W25QXX_Wait_Busy();

}
/**
 * @brief    W25QXX写禁止,将WEL清零
 * @param    none
 * @retval    none
 */
void W25QXX_Write_Disable(void)
{
    uint8_t cmd = WRITE_DISABLE_CMD;

    SPI_FLASH_CS_LOW();
    
    SPI_Transmit(&cmd, 1);
    
    SPI_FLASH_CS_HIGH();
    
    W25QXX_Wait_Busy();
}
/**
 * @brief    W25QXX擦除一个扇区
 * @param   sector_addr    —— 扇区地址 根据实际容量设置
 * @retval  none
 * @note    阻塞操作
 */
void W25QXX_Erase_Sector(uint32_t sector_addr)
{   
    W25QXX_Write_Enable();  //擦除操作即写入0xFF，需要开启写使能
    W25QXX_Wait_Busy();     //等待写使能完成
   
    /* 使能片选 */
    SPI_FLASH_CS_LOW();
    
    /* 发送扇区擦除指令*/
    SPI_FLASH_SendByte(SECTOR_ERASE_CMD);
    /*发送擦除扇区地址的高位*/
    SPI_FLASH_SendByte((sector_addr & 0xFF0000) >> 16);
    /* 发送擦除扇区地址的中位 */
    SPI_FLASH_SendByte((sector_addr & 0xFF00) >> 8);
    /* 发送擦除扇区地址的低位 */
    SPI_FLASH_SendByte(sector_addr & 0xFF);
    
    /* 取消片选 */
    SPI_FLASH_CS_HIGH();
    
    W25QXX_Wait_Busy();       //等待扇区擦除完成
}
/**
 * @brief    页写入操作
 * @param    dat —— 要写入的数据缓冲区首地址
 * @param    WriteAddr —— 要写入的地址
 * @param   byte_to_write —— 要写入的字节数（0-256）
 * @retval    none
 */
void W25QXX_PageProgram(uint8_t* dat, uint32_t WriteAddr, uint16_t nbytes)
{
    uint8_t cmd = PAGE_PROGRAM_CMD;
    
//    WriteAddr <<= 8;
    
    W25QXX_Write_Enable();
    
    /* 使能片选 */
    SPI_FLASH_CS_LOW();
    
    SPI_Transmit(&cmd, 1);

//    SPI_Transmit((uint8_t*)&WriteAddr, 3);
    uint8_t addr;
    HAL_StatusTypeDef status;
    /* 发送 读 地址高位 */
    addr = (WriteAddr & 0xFF0000) >> 16;
    status = SPI_Transmit(&addr, 1);
    /* 发送 读 地址中位 */
    addr = (WriteAddr & 0xFF00) >> 8;
    status = SPI_Transmit(&addr, 1);
    /* 发送 读 地址低位 */
    addr = WriteAddr & 0xFF;
    status = SPI_Transmit(&addr, 1);
    (void) status;
    
    SPI_Transmit(dat, nbytes);
    
    /* 取消片选 */
    SPI_FLASH_CS_HIGH();
    
    W25QXX_Wait_Busy();
}
/**
 * @brief  对FLASH写入数据，调用本函数写入数据前需要先擦除扇区
 * @param   pBuffer，要写入数据的指针
 * @param  WriteAddr，写入地址
 * @param  NumByteToWrite，写入数据长度
 * @retval 无
 */
void W25QXX_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    uint8_t NumOfPage = 0;
    uint8_t NumOfSingle = 0;
    uint8_t Addr = 0;
    uint8_t count = 0;
    uint8_t temp = 0;
    
    /*mod运算求余，若writeAddr是SPI_FLASH_PageSize整数倍，运算结果Addr值为0*/
    Addr = WriteAddr % SPI_FLASH_PageSize;
    
    /*差count个数据值，刚好可以对齐到页地址*/
    count = SPI_FLASH_PageSize - Addr;  
    /*计算出要写多少整数页*/
    NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
    /*mod运算求余，计算出剩余不满一页的字节数*/
    NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

    /* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
    if(Addr == 0) 
    {
        /* NumByteToWrite < SPI_FLASH_PageSize */
        if(NumOfPage == 0) 
        {
            W25QXX_PageProgram(pBuffer, WriteAddr, NumByteToWrite);
        }
        /* NumByteToWrite > SPI_FLASH_PageSize */
        else
        {
            /*先把整数页都写了*/
            while(NumOfPage--)
            {
                W25QXX_PageProgram(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                WriteAddr +=  SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            
            /*若有多余的不满一页的数据，把它写完*/
            W25QXX_PageProgram(pBuffer, WriteAddr, NumOfSingle);
        }
    }
    /* 若地址与 SPI_FLASH_PageSize 不对齐  */
    else 
    {
        /* NumByteToWrite < SPI_FLASH_PageSize */
        if(NumOfPage == 0) 
        {
            /*当前页剩余的count个位置比NumOfSingle小，写不完*/
            if(NumOfSingle > count) 
            {
                temp = NumOfSingle - count;
                
                /*先写满当前页*/
                W25QXX_PageProgram(pBuffer, WriteAddr, count);
                WriteAddr += count;
                pBuffer += count;
                
                /*再写剩余的数据*/
                W25QXX_PageProgram(pBuffer, WriteAddr, temp);
            }
            /*当前页剩余的count个位置能写完NumOfSingle个数据*/
            else
            {               
                W25QXX_PageProgram(pBuffer, WriteAddr, NumByteToWrite);
            }
        }
        /* NumByteToWrite > SPI_FLASH_PageSize */
        else
        {
            /*地址不对齐多出的count分开处理，不加入这个运算*/
            NumByteToWrite -= count;
            NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
            NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

            W25QXX_PageProgram(pBuffer, WriteAddr, count);
            WriteAddr +=  count;
            pBuffer += count;
            
            /*把整数页都写了*/
            while(NumOfPage--)
            {
                W25QXX_PageProgram(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                WriteAddr +=  SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            /*若有多余的不满一页的数据，把它写完*/
            if(NumOfSingle != 0)
            {
                W25QXX_PageProgram(pBuffer, WriteAddr, NumOfSingle);
            }
        }
    }
}
 /**
  * @brief  读取FLASH数据
  * @param  pBuffer，存储读出数据的指针
  * @param   ReadAddr，读取地址
  * @param   NumByteToRead，读取数据长度
  * @retval 无
  */
void W25QXX_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{  
    W25QXX_Wait_Busy();
    
    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();

    /* 发送 读 指令 */
    uint8_t cmd = READ_DATA_CMD;
    SPI_Transmit(&cmd, 1);

// 不知道为什么连起来发不行
//    ReadAddr = ReadAddr << 8;
//    SPI_Transmit((uint8_t*)&ReadAddr, 3);
    
    uint8_t addr;
    HAL_StatusTypeDef status;
    /* 发送 读 地址高位 */
    addr = (ReadAddr & 0xFF0000) >> 16;
    status = SPI_Transmit(&addr, 1);
    /* 发送 读 地址中位 */
    addr = (ReadAddr& 0xFF00) >> 8;
    status = SPI_Transmit(&addr, 1);
    /* 发送 读 地址低位 */
    addr = ReadAddr & 0xFF;
    status = SPI_Transmit(&addr, 1);
    
    if(HAL_OK == status)
    {
        SPI_Receive(pBuffer, NumByteToRead);
    }

    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();
}
